#include "ItemModel.h"
#include "ItemRoot.h"
#include "Item.h"
#include "ItemReference.h"
//#include "Defines.h"



#include <QDebug>



ItemModel::ItemModel(QObject* parent)
    : QAbstractItemModel(parent)
{
}



ItemModel::ItemModel(ItemRoot *root, QObject *parent)
    : QAbstractItemModel(parent)
{
    setRootItem(root);
}



ItemModel::~ItemModel()
{
}



AbstractItem* ItemModel::indexItem(const QModelIndex& index) const
{
    return dynamic_cast<AbstractItem*>(static_cast<QObject*>(index.internalPointer()));
}


ItemRoot* ItemModel::rootItem() const
{
    return m_root.data();
}



void ItemModel::setRootItem(ItemRoot* root)
{
    emit layoutAboutToBeChanged();
    if (!m_root.isNull())
    {
        disconnect(m_root.data(), SIGNAL(sigDirty()), this, SIGNAL(layoutChanged()));
    }

    m_root = dynamic_cast<ItemRoot*>(root);
    if (!m_root.isNull())  connect(m_root.data(), SIGNAL(sigDirty()), this, SIGNAL(layoutChanged()));

    emit layoutChanged();
}



QModelIndex ItemModel::index(int row, int column, const QModelIndex &parent) const
{
    if (m_root.isNull()) return QModelIndex();
    if (!hasIndex(row, column, parent)) return QModelIndex();

    AbstractItem* parentItem = 0;
    if (!parent.isValid())
    {
        parentItem = m_root.data();
    }
    else
    {
        parentItem = indexItem(parent);
    }

    if (!parentItem || parentItem->itemCount() <= row) return QModelIndex();

    AbstractItem* item = parentItem->itemAt(row);
    if (!item) return QModelIndex();

    return createIndex(row, column, item);
}



QModelIndex ItemModel::parent(const QModelIndex &child) const
{
    if (m_root.isNull() || !child.isValid()) return QModelIndex();

    AbstractItem* childItem = indexItem(child);
    if (!childItem || childItem == rootItem()) return QModelIndex();

    AbstractItem* item = qobject_cast<AbstractItem*>(childItem->parent());
    if (!item || !item->parent() || item == rootItem()) return QModelIndex();


    int row = item->parent()->children().indexOf(item);
    return createIndex(row, 0, item);
}



QModelIndex ItemModel::referencedIndex(const QModelIndex& reference) const
{
    if (!rootItem() || !reference.isValid()) return QModelIndex();

    QModelIndex searchIndex = index(0, AbstractItem::IdData, QModelIndex());
    if (!searchIndex.isValid()) return QModelIndex();

    QModelIndex typeIndex = reference.sibling(reference.row(), AbstractItem::CategoryData);
    if (!typeIndex.isValid()) return QModelIndex();

    int type = typeIndex.data(Qt::EditRole).toInt();
    if (!IS_REFERENCE_TYPE(type)) return QModelIndex();

    QModelIndex referenceIndex = reference.sibling(reference.row(), ItemReference::ReferenceData);
    if (!referenceIndex.isValid()) return QModelIndex();

    QModelIndexList foundIndexes = match(searchIndex,
                                         Qt::EditRole,
                                         referenceIndex.data(Qt::EditRole),
                                         1,
                                         Qt::MatchExactly | Qt::MatchRecursive);

    if (foundIndexes.isEmpty()) return QModelIndex();

    return foundIndexes.first();

}


int ItemModel::rowCount(const QModelIndex &parent) const
{
    if (m_root.isNull()) return 0;

    AbstractItem* item = 0;

    if (!parent.isValid())
    {
        item = m_root.data();
    }
    else
    {
        item = indexItem(parent);
    }

    if (!item) return 0;

    return item->itemCount();
}



int ItemModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);

    if (m_root.isNull()) return 0;

    int ret = 0;
    foreach (AbstractItem* item, m_root->findChildren<AbstractItem*>())
    {
        if (!item) continue;
        if (item->fieldCount() > ret) ret = item->fieldCount();
    }

    return ret;
}



QVariant ItemModel::data(const QModelIndex &index, int role) const
{
    if (role != Qt::DisplayRole && role != Qt::EditRole) return QVariant();
    if (m_root.isNull() || !index.isValid()) return QVariant();

    AbstractItem* item = indexItem(index);

    if (!item || item == rootItem()) return QVariant();

    return item->data(index.column(), role);

}



bool ItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (!index.isValid() || role != Qt::EditRole) return false;

    AbstractItem* item = indexItem(index);
    if (!item) return false;

    //emit layoutAboutToBeChanged();
    item->setData(index.column(), value);
    emit dataChanged(index, index);
    //emit layoutChanged();
    return true;
}



QVariant ItemModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    Q_UNUSED(orientation);

    if (role != Qt::DisplayRole) return QVariant();

    if (section < 0 || section >= columnCount(QModelIndex())) return QVariant();

    // todo: all data type names to common defines.
    switch (section)
    {
    case AbstractItem::IdData:          return tr("Id");
    case AbstractItem::NameData:        return tr("Name");
    case AbstractItem::DescData:        return tr("Description");
    case AbstractItem::CategoryData:        return tr("Type");

    case Item::ValueData:               return tr("Value");
//    case ItemReference::ReferenceType:       return tr("Reference Type");
    case ItemReference::ReferenceData:        return tr("Referenced Item");
    default:                            return tr("Unknown Field");
    }
}



Qt::ItemFlags ItemModel::flags(const QModelIndex &index) const
{
    switch (index.column())
    {
    case AbstractItem::IdData:      return QAbstractItemModel::flags(index);
    case AbstractItem::CategoryData:    return QAbstractItemModel::flags(index);
    }

    AbstractItem* item = indexItem(index);
    if (item->rootItem() != rootItem()) return QAbstractItemModel::flags(index);

    return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
}

int ItemModel::addItem(const QModelIndex& parent)
{
    if (m_root.isNull()) return -1;

    AbstractItem* parentItem = indexItem(parent);
    if (!parentItem) parentItem = m_root.data();

    if (IS_REFERENCE(parentItem)) return -1;


    AbstractItem* item = new Item(tr("New Item"), parentItem);
    int row = parentItem->children().indexOf(item);

    beginInsertRows(parent, row, row);
    QModelIndex first = index(row, 0, parent);
    QModelIndex last = index(row, columnCount(parent), parent);
    endInsertRows();

    emit dataChanged(first, last);
    return row;
}

int ItemModel::addReference(const QModelIndex &parent)
{
    if (m_root.isNull()) return -1;

    AbstractItem* parentItem = indexItem(parent);
    if (!parentItem || IS_REFERENCE(parentItem)) return -1;

    AbstractItem* item = new ItemReference(parentItem);
    int row = parentItem->children().indexOf(item);

    beginInsertRows(parent, row, row);
    QModelIndex first = index(row, 0, parent);
    QModelIndex last = index(row, columnCount(parent), parent);
    endInsertRows();

    emit dataChanged(first, last);
    return row;
}



bool ItemModel::removeRows(int row, int count, const QModelIndex &parent)
{
    Q_UNUSED(count);
    return removeRow(row, parent);
}


bool ItemModel::removeRow(int row, const QModelIndex &parent)
{
    if (m_root.isNull()) return false;

    AbstractItem* item = 0;
    if (!parent.isValid())
    {
        item = m_root.data();
    }
    else
    {
        item = indexItem(parent);
    }

    if (!item) return false;

    // todo: remove layout change signals and create slot for rowsAboutToBeRemoved() signal.
    beginRemoveRows(parent, row, row);
    item->removeAt(row);
    endRemoveRows();

    emit dataChanged(index(0,0,parent), index(rowCount(parent), columnCount(parent), parent));
    return true;
}



//QVariant ItemModel::siblingData(const int& col, const QModelIndex& index, const int& role) const
//{
//    if (!index.isValid()) return QVariant();

//    QModelIndex sibling = index.sibling(index.row(), col);
//    if (!sibling.isValid()) return QVariant();

//    return sibling.data(role);
//}
